package org.axed.user.client;

import java.util.ArrayList;

import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;

import com.google.gwt.core.client.GWT;


/**
 * Handles the logic that is needed in HTML to determine
 * when to set a softspace and when a hardspace.
 *
 * When more than one space appears, all except the last one
 * have to be replaced with hard spaces, also terminating
 * spaces need to be replaced.
 *
 * e.g. "Hello World" -- "Hello World"
 *      "Hello  World "  -- "Hello&nbsp; World&nbsp;";
 */
public class SpaceString {
	/**
 	 * A hardspace will be replaced with this string.
 	 */
	//static String hardSpace = "&middot;";
	static String hardSpace = "&nbsp;";

	/**
 	 * Holds the text for the line.
 	 */
	StringBuffer text;

	/**
	 * The html string.
 	 */
	public HtmlString htmlText;
	
	/**
	 * Constructor.
	 */
	public SpaceString() {
		text = new StringBuffer();
		this.htmlText = new HtmlString();
	}

	/**
	 * Constructor.
	 */
	public SpaceString(String text) {
		this.text = new StringBuffer(text);
		String s = stringToHTML(text);
		htmlText = new HtmlString(s);
	}


	/**
 	 * Adds a new char at the end.
 	 */
	public void add(char c) {
		text.append(c);
		switch (c) {
		case ' ' :
			htmlText.appendCol(hardSpace);
			break;
		default :
			if (text.length() > 2 && text.charAt(text.length() - 2) == ' ') {
				// change the car before this to a soft space.
				htmlText.replaceCol(" ", text.length() - 2);
			}
			htmlText.appendCol(charToHTML(c));
			break;
		}
	}

	/**
	 * Returns the text character at column c
	 */
	public char charAt(int col) {
		return text.charAt(col);
	}


	/**
	 * Inserts a character at a sepecfic column.
	 *
	 * @param c    The character to insert.
	 * @param col  The column to insert the character at.
	 */
	public void insert(char c, int col) {
		if (col == size()) {
			add(c);
			return;
		}
		boolean nhsp1 = col == 0 ? false : needsHardspace(col - 1);
		boolean nhsp2 = needsHardspace(col);

		text.insert(col, c);

		if (needsHardspace(col)) {
			htmlText.insertCol(hardSpace, col);
		} else {
			htmlText.insertCol(charToHTML(c), col);
		}

		boolean nhsp1_2 = col == 0 ? false : needsHardspace(col - 1);
		boolean nhsp2_2 = needsHardspace(col + 1);

		if (nhsp1 != nhsp1_2) {
			htmlText.replaceCol(nhsp1_2 ? hardSpace : " ", col - 1);
		}
		if (nhsp2 != nhsp2_2) {
			htmlText.replaceCol(nhsp2_2 ? hardSpace : " ", col + 1);
		}
	}

	/**
	 * Inserts a string at a sepecfic column.
	 *
	 * @param str  The character to insert.
	 * @param col  The column to insert the character at.
	 */
	public void insert(String str, int col) {
		text.insert(col, str);
		rebuildHTML();
	}


	/**
	 * Joins this String with another one.
	 *
	 * @return this
	 */
	public SpaceString join (SpaceString joinee) {
		text.append(joinee.text);
		rebuildHTML();
		return this;
	}

	/**
	 * Returns the length.
	 */
	public int length() {
		return text.length();
	}
	
	/**
	 * Removes a character at col.
	 *
	 * @param col   The position of the character to remove.
	 */
	public void remove(int col) {
		if (col + 1 == size()) {
			removeLast();
			return;
		}
		
		// Check if a hardspace is needed before and after the deletion point.
		boolean nhsp1 = col == 0 ? false : needsHardspace(col - 1);
		boolean nhsp2 = needsHardspace(col + 1);

		// Do the deletion
		text.deleteCharAt(col);
		htmlText.removeCol(col);

		// Recheck the need of hardspace before and after the deletion point,
		// after the caracter has been deleded.
		boolean nhsp1_2 = col == 0 ? false : needsHardspace(col - 1);
		boolean nhsp2_2 = needsHardspace(col);

		// If this differs, change the necessary hardspace to a softspace and viceversa.
		if (nhsp1 != nhsp1_2) {
			htmlText.replaceCol(nhsp1_2 ? hardSpace : " ", col - 1);
		}
		if (nhsp2 != nhsp2_2) {
			htmlText.replaceCol(nhsp2_2 ? hardSpace : " ", col);
		}
	}

	/**
	 * Removes characters from start column to end coloumn.
	 *
	 * @param start     the column to start deletion from.
	 * @param end       the column to end deletion at.
	 *
	 * @return text deleted
	 */
	public String remove(int start, int end) {
		boolean atRightEnd = false;
		if (end == text.length()) {
			atRightEnd = true;
		}
	
		// Check if a hardspace is needed before and after the deletion point.
		boolean nhsp1 = start == 0 ? false : needsHardspace(start - 1);
		boolean nhsp2 = !atRightEnd && needsHardspace(end);

		String ret = text.substring(start, end);
		// Do the deletion
		text.delete(start, end);
		htmlText.removeCols(start, end);

		// Recheck the need of hardspace before and after the deletion point,
		// after the caracter has been deleded.
		boolean nhsp1_2 = start == 0 ? false : needsHardspace(start - 1);
		boolean nhsp2_2 = !atRightEnd && needsHardspace(start);

		// If this differs, change the necessary hardspace to a softspace and viceversa.
		if (nhsp1 != nhsp1_2) {
			htmlText.replaceCol(nhsp1_2 ? hardSpace : " ", start - 1);
		}
		if (nhsp2 != nhsp2_2) {
			htmlText.replaceCol(nhsp2_2 ? hardSpace : " ", start);
		}
		return ret;
	}

	/**
	 * Removes the last characer.
	 */
	public void removeLast() {
		int l = text.length() - 1;

		// Check if a hardspace is needed before and after the deletion point.
		boolean nhsp1 = l == 0 ? false : needsHardspace(l - 1);

		// Do the deletion
		text.deleteCharAt(l);
		htmlText.removeCol(l);

		// Recheck the need of hardspace before deletion point,
		boolean nhsp1_2 = l == 0 ? false : needsHardspace(l - 1);
		if (nhsp1 != nhsp1_2) {
			htmlText.replaceCol(nhsp1_2 ? hardSpace : " ", l - 1);
		}
	}      

	/**
	 * Sets the text to s
	 */
	public void set(String s) {
		text.replace(0, text.length(), s);
		rebuildHTML();
	}

	/**
	 * Returns the size of the line
	 */
	public int size() { 
		return text.length();
	}

	/**
	 * Splits this String into two.
	 *
	 * @param col   The column to split the line at.
	 *
	 * @return The new line.
	 */
	public SpaceString split(int col) {
		SpaceString ht = new SpaceString(text.substring(col, text.length()));
		text.delete(col, text.length());
		rebuildHTML();
		return ht;
	}

	public String text()
	{
		return text.toString();
	}

//privates

	/**
	 * Turn a string to HTML
	 */
	public static String stringToHTML(String s) {
		s = s.replaceAll("&", "&amp;");
		s = s.replaceAll(">", "&gt;");
		s = s.replaceAll("<", "&lt;");
		// replace to hardspaces if a space follows, as well as at begging or at end of line
		s = s.replaceAll("( (?= )|^ | $)", hardSpace);
		return s;
	}

	/**
	 * Returns the HTML representation of a char
	 */
	public static String charToHTML(char c) {
		switch (c) {
		case '&' : return "&amp;";
		case '<' : return "&lt;";
		case '>' : return "&gt;";
		default  : return "" + c;
		}
	}


	/**
	 * Returns true if the character at position pos needs a HTML hardspace
	 */
	private boolean needsHardspace(int col) {
		if (text.charAt(col) != ' ') {	    // if its not space it will never be a hard space, duh
			return false;
		}
		if (col == 0 ||                     // on start of line its always a hard space 
		    col == text.length() - 1 ||     // same on end of line
		    text.charAt(col + 1) == ' ') {  // or if follow by a space
		    return true;
		}
		return false;
	}

	/**
	 * Complelety recreates the htmlText string from the text string.
	 */ 
	private void rebuildHTML() {
		htmlText.clear();

		for (int i = 0; i < text.length(); i++) {
			char c = text.charAt(i);
			if (c == ' ' && (
				i == 0 ||
				i + 1 < text.length() && text.charAt(i + 1) == ' ' ||
				i + 1 == text.length())) {
				htmlText.appendCol(hardSpace);
			} else {
				//htmlText.appendCol("" + c);
				htmlText.appendCol(charToHTML(c));
			}
		}
	}
}

